package io.gitee.xjt2016.modules.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import io.gitee.xjt2016.modules.common.BaseController;
import io.gitee.xjt2016.modules.common.WebResponse;
import io.gitee.xjt2016.modules.domain.ApiSendInfo;
import io.gitee.xjt2016.modules.domain.ApiSendRequest;
import io.gitee.xjt2016.modules.domain.ApiSendResponse;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

@Log4j2
@Controller
@RequestMapping(value = "/api")
public class ApiController extends BaseController {

    @ResponseBody
    @RequestMapping(value = {"/send"})
    public Object send(ApiSendRequest request) throws Exception {

        HttpRequest httpRequest = HttpUtil.createRequest(Method.valueOf(request.getMethod()), request.getUrl());
        httpRequest.addHeaders(request.getHeaders());
        httpRequest.form(request.getParams());
        if (StringUtils.isNotBlank(request.getBody())) {
            httpRequest.body(request.getBody());
        }
        HttpResponse httpResponse = httpRequest.execute();

        Map<String, Object> result = new HashMap<>();
        result.put("request", FUNCTION.apply(request));

        ApiSendInfo apiSendInfo = new ApiSendInfo();
        apiSendInfo.setUrl(request.getUrl());

        long startTime = System.currentTimeMillis();
        try {
            ApiSendResponse response = convert2ApiSendResponse(httpRequest, httpResponse);
            result.put("response", FUNCTION.apply(response));
            apiSendInfo.setStatus(response.getStatus());
            apiSendInfo.setResponseBodySize(httpResponse.bodyBytes().length);
        } catch (Throwable throwable) {
            log.error(throwable.getMessage(), throwable);
        } finally {
            long endTime = System.currentTimeMillis();
            apiSendInfo.setTotalResponseTime(endTime - startTime);
        }

        result.put("info", FUNCTION.apply(apiSendInfo));

        WebResponse webResponse = new WebResponse();
        webResponse.setData(result);
        return webResponse;
    }

    public static final Function<Map<String, Object>, List<Map<String, Object>>> FUNCTION2 = map -> map.entrySet().stream().map(entry -> {
        Map<String, Object> params = new HashMap<>();
        params.put("key", entry.getKey());
        params.put("value", entry.getValue());
        return params;
    }).collect(Collectors.toList());

    public static final Function<Object, List<Map<String, Object>>> FUNCTION = o -> FUNCTION2.apply(BeanUtil.beanToMap(o));

    public ApiSendResponse convert2ApiSendResponse(HttpRequest httpRequest, HttpResponse httpResponse) {
        ApiSendResponse apiSendResponse = new ApiSendResponse();

        apiSendResponse.setUrl(httpRequest.getUrl());
        apiSendResponse.setStatus(httpResponse.getStatus());


        Map<String, Object> headerMap = new HashMap<>();
        httpResponse
                .headers()
                .entrySet()
                .stream()
                .filter(entry -> entry.getKey() != null).forEach(new Consumer<Map.Entry<String, List<String>>>() {
            @Override
            public void accept(Map.Entry<String, List<String>> stringListEntry) {
                headerMap.put(stringListEntry.getKey(), stringListEntry.getValue().get(0));
            }
        });
        apiSendResponse.setHeaders(FUNCTION2.apply(headerMap));

        apiSendResponse.setCookies(httpResponse.getCookieStr());
        apiSendResponse.setBody(httpResponse.body());
        apiSendResponse.setCharset(httpResponse.charset());
        apiSendResponse.setHttpVersion(httpResponse.httpVersion());
        apiSendResponse.setGzip(httpResponse.isGzip());
        apiSendResponse.setOk(httpResponse.isOk());
        return apiSendResponse;
    }

}
